class_name GameplayStateMachine
extends GameplayState
## Gameplay Hierarchy State Machine
## Version: 0.1.0

##
var blackboard: Dictionary

##
var current_state_name: String

##
var states: Dictionary = {}


func get_state(state_path: String) -> GameplayState:
	if state_path.is_empty():
		return self
	else:
		var state_names := state_path.split(".")
		var from_state_name = state_names[0]
		
		state_names.remove_at(0)

		return states[from_state_name].get_state(".".join(state_names))


func get_current_state() -> GameplayState:
	return states[current_state_name]


func get_current_state_name() -> String:
	return name + "." + states[current_state_name].get_current_state_name()


func dispatch_event(event_name: String) -> void:
	if states.has("AnyState"):
		for transition: EventStateTransition in states["AnyState"].transitions:
			if transition and\
			   transition.event_name == event_name and\
			   states[current_state_name].change_state(transition.try_transition(), transition.on_transit):
				return

	super(event_name)


func add_transition(from_state_name: String, transition: GameplayStateTransition) -> void:
	assert(states.has(from_state_name), "From state is not a child of this state machine!")
	assert(transition, "Transition should not be empty!")

	states[from_state_name].transitions.append(transition)


func remove_transition(from_state_name: String, transition: GameplayStateTransition) -> void:
	assert(states.has(from_state_name), "From state is not a child of this state machine!")
	assert(transition, "Transition should not be empty!")

	states[from_state_name].transitions.erase(transition)


func add_state(state: GameplayState) -> void:
	assert(state, "Added state should not be null!")
	assert(not states.has(state.name), "Already has a state with the name: {0}!".format([state.name]))
	
	if state.get_parent():
		state.reparent(self)
	else:
		add_child(state)

	states[state.name] = state

	state.on_setup()


func remove_state(state: GameplayState) -> void:
	if not states.has(state.name):
		return

	if current_state_name == state.name:
		return

	states.erase(state.name)


func on_setup() -> void:
	for state: GameplayState in get_children():
		add_state(state)
	
	var ghost_state := GameplayState.new()
	ghost_state.name = "GhostState"
	add_state(ghost_state)

	var any_state := GameplayState.new()
	any_state.name = "AnyState"
	add_state(any_state)
	
	current_state_name = "GhostState"
	
	super()


func on_enter() -> void:
	super()

	current_state_name = "GhostState"

	assert(states.has(current_state_name), "Current state is not in the states dictionary!")
	states[current_state_name].on_enter()


func on_update(delta: float) -> void:
	if try_all_transitions():
		return
	
	super(delta)
	
	if states.has("AnyState"):
		states["AnyState"].on_update(delta)

	assert(states.has(current_state_name), "Current state is not in the states dictionary!")
	states[current_state_name].on_update(delta)


func on_exit() -> void:
	assert(states.has(current_state_name), "Current state is not in the states dictionary!")
	states[current_state_name].on_exit()

	super()
